pull: Add support for basic auth
authorColin Walters <walters@verbum.org>
Wed, 27 Sep 2017 02:02:25 +0000 (22:02 -0400)
committerColin Walters <walters@verbum.org>
Mon, 2 Dec 2019 22:48:33 +0000 (22:48 +0000)
This has been requested a few times for people delivering
OSTree content and wanting to do access control.

Makefile-tests.am
src/libostree/ostree-fetcher-curl.c
src/ostree/ostree-trivial-httpd.c
tests/test-pull-basicauth.sh [new file with mode: 0755]

index f5a65278117ac69593ece4531a2eaed2ed537020..fc2f2d910dba4fdd3ae17c4d606ef3556d44c978 100644 (file)
@@ -84,6 +84,7 @@ _installed_or_uninstalled_test_scripts = \
        tests/test-pull-metalink.sh \
        tests/test-pull-summary-sigs.sh \
        tests/test-pull-resume.sh \
+       tests/test-pull-basicauth.sh \
        tests/test-pull-repeated.sh \
        tests/test-pull-untrusted.sh \
        tests/test-pull-override-url.sh \
index 294b20781ea00cb0bb1a0020873b20b49e8c0d98..fdf8a2ef00de878a147024e1e17c531c371082b9 100644 (file)
@@ -261,9 +261,8 @@ destroy_and_unref_source (GSource *source)
 }
 
 static char *
-request_get_uri (FetcherRequest *req, guint idx)
+request_get_uri (FetcherRequest *req, SoupURI *baseuri)
 {
-  SoupURI *baseuri = req->mirrorlist->pdata[idx];
   if (!req->filename)
     return soup_uri_to_string (baseuri, FALSE);
   { g_autofree char *uristr = soup_uri_to_string (baseuri, FALSE);
@@ -715,7 +714,8 @@ initiate_next_curl_request (FetcherRequest *req,
 
   g_assert_cmpint (req->idx, <, req->mirrorlist->len);
 
-  { g_autofree char *uri = request_get_uri (req, req->idx);
+  SoupURI *baseuri = req->mirrorlist->pdata[req->idx];
+  { g_autofree char *uri = request_get_uri (req, baseuri);
     curl_easy_setopt (req->easy, CURLOPT_URL, uri);
   }
 
@@ -771,6 +771,12 @@ initiate_next_curl_request (FetcherRequest *req,
   if ((self->config_flags & OSTREE_FETCHER_FLAGS_TRANSFER_GZIP) > 0)
     curl_easy_setopt (req->easy, CURLOPT_ACCEPT_ENCODING, "");
 
+  /* If we have e.g. basic auth in the URL string, let's honor that */
+  const char *username = soup_uri_get_user (baseuri);
+  curl_easy_setopt (req->easy, CURLOPT_USERNAME, username);
+  const char *password = soup_uri_get_password (baseuri);
+  curl_easy_setopt (req->easy, CURLOPT_PASSWORD, password);
+
   /* We should only speak HTTP; TODO: only enable file if specified */
   curl_easy_setopt (req->easy, CURLOPT_PROTOCOLS, (long)(CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FILE));
   /* Picked the current version in F25 as of 20170127, since
index 1b7aa2a2d73c80bbbe7e98866eb2700e79373f95..5d3a004ee420e8da8fd02494d8bc753abea777ac 100644 (file)
@@ -50,6 +50,7 @@ static int opt_random_408s_max = 100;
 static gint opt_port = 0;
 static gchar **opt_expected_cookies;
 static gchar **opt_expected_headers;
+static gboolean opt_require_basic_auth;
 
 static guint emitted_random_500s_count = 0;
 static guint emitted_random_408s_count = 0;
@@ -71,6 +72,7 @@ static GOptionEntry options[] = {
   { "port", 'P', 0, G_OPTION_ARG_INT, &opt_port, "Use the specified TCP port", "PORT" },
   { "port-file", 'p', 0, G_OPTION_ARG_FILENAME, &opt_port_file, "Write port number to PATH (- for standard output)", "PATH" },
   { "force-range-requests", 0, 0, G_OPTION_ARG_NONE, &opt_force_ranges, "Force range requests by only serving half of files", NULL },
+  { "require-basic-auth", 0, 0, G_OPTION_ARG_NONE, &opt_require_basic_auth, "Require username foouser, password barpw", NULL },
   { "random-500s", 0, 0, G_OPTION_ARG_INT, &opt_random_500s_percentage, "Generate random HTTP 500 errors approximately for PERCENTAGE requests", "PERCENTAGE" },
   { "random-500s-max", 0, 0, G_OPTION_ARG_INT, &opt_random_500s_max, "Limit HTTP 500 errors to MAX (default 100)", "MAX" },
   { "random-408s", 0, 0, G_OPTION_ARG_INT, &opt_random_408s_percentage, "Generate random HTTP 408 errors approximately for PERCENTAGE requests", "PERCENTAGE" },
@@ -474,6 +476,13 @@ httpd_callback (SoupServer *server, SoupMessage *msg,
     soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
 }
 
+static gboolean
+basic_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
+                     const char *username, const char *password, gpointer data)
+{
+       return g_str_equal (username, "foouser") && g_str_equal (password, "barpw");
+}
+
 static void
 on_dir_changed (GFileMonitor  *mon,
                 GFile *file,
@@ -571,6 +580,15 @@ run (int argc, char **argv, GCancellable *cancellable, GError **error)
                             SOUP_SERVER_SERVER_HEADER, "ostree-httpd ",
                             NULL);
 #endif
+  if (opt_require_basic_auth)
+    {
+      glnx_unref_object SoupAuthDomain *auth_domain =
+        soup_auth_domain_basic_new (SOUP_AUTH_DOMAIN_REALM, "auth-test",
+                                    SOUP_AUTH_DOMAIN_ADD_PATH, "/",
+                                    SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, basic_auth_callback,
+                                    NULL);
+      soup_server_add_auth_domain (server, auth_domain);
+    }
 
   soup_server_add_handler (server, NULL, httpd_callback, app, NULL);
   if (opt_port_file)
diff --git a/tests/test-pull-basicauth.sh b/tests/test-pull-basicauth.sh
new file mode 100755 (executable)
index 0000000..4a2622a
--- /dev/null
@@ -0,0 +1,55 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 Colin Walters <walters@verbum.org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+set -euo pipefail
+
+. $(dirname $0)/libtest.sh
+
+setup_fake_remote_repo1 "archive" "" "--require-basic-auth"
+
+echo '1..3'
+
+repopath=${test_tmpdir}/ostree-srv/gnomerepo
+cp -a ${repopath} ${repopath}.orig
+
+cd ${test_tmpdir}
+rm repo -rf
+ostree_repo_init repo
+unauthaddress=$(cat httpd-address)
+badauthaddress=$(echo $unauthaddress | sed -e 's,http://,http://foo:bar@,')
+goodauthaddress=$(echo $unauthaddress | sed -e 's,http://,http://foouser:barpw@,')
+${CMD_PREFIX} ostree --repo=repo remote add --set=gpg-verify=false origin-unauth ${unauthaddress}/ostree/gnomerepo
+${CMD_PREFIX} ostree --repo=repo remote add --set=gpg-verify=false origin-badauth ${badauthaddress}/ostree/gnomerepo
+${CMD_PREFIX} ostree --repo=repo remote add --set=gpg-verify=false origin-goodauth ${goodauthaddress}/ostree/gnomerepo
+
+if ${CMD_PREFIX} ostree --repo=repo pull origin-unauth main 2>err.txt; then
+    fatal "Pulled via unauth"
+fi
+assert_file_has_content err.txt "401"
+echo "ok unauth"
+rm -f err.txt
+if ${CMD_PREFIX} ostree --repo=repo pull origin-badauth main 2>err.txt; then
+    fatal "Pulled via badauth"
+fi
+assert_file_has_content err.txt "401"
+rm -f err.txt
+echo "ok badauth"
+
+${CMD_PREFIX} ostree --repo=repo pull origin-goodauth main
+echo "ok basic auth"